package cn.demoncat.util.quartz;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.lang.LocalDateUtil;
import cn.demoncat.util.quartz.constant.JobConstant;
import cn.demoncat.util.quartz.constant.JobConstant.TriggerType;
import cn.demoncat.util.quartz.constant.JobMsg;
import org.quartz.*;
import org.quartz.Trigger.TriggerState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * Quartz调度工具
 * 
 * @author 延晓磊
 *
 * @since 2017年9月14日
 */
@Component("schedulerUtil")
public class SchedulerUtil {

	private static final Logger LOG = LoggerFactory.getLogger(SchedulerUtil.class);

	@Resource
	private Scheduler scheduler;

	/**
	 * 添加任务
	 *
	 * @param jobName
	 * @param jobGroup
	 * @param triggerType		任务触发模式：组合time,timeout,cron,count,endTime,forever
	 * @param triggerTime		任务触发时机：指定时间
	 * @param triggerEndTime	任务终止时间
	 * @param triggerTimeout	任务触发时机：timeout秒后
	 * @param triggerCount		任务触发次数
	 * @param triggerCron		任务执行时机：CRON
	 * @param executorHandler	任务执行器(BeanName，实现execute(param)方法)
	 * @param executorParam		任务执行参数，由Handler自行解析
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public void addJob(String jobName, String jobGroup, String triggerType, LocalDateTime triggerTime, LocalDateTime triggerEndTime, Integer triggerTimeout, Integer triggerCount, String triggerCron, String executorHandler, String executorParam){
		// 检查任务是否存在
		if (checkExists(jobName, jobGroup)) {
			LOG.error(JobMsg.REPEAT, jobGroup, jobName);
			throw new BizRuntimeException(JobMsg.JOB_REPEAT_ERROR);
		}
		// JobKey
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		JobKey jobKey = new JobKey(jobName, jobGroup);
		// 创建Trigger
		Trigger trigger = getTrigger(triggerKey, triggerType, triggerTime, triggerEndTime, triggerTimeout, triggerCount, triggerCron);
		// 创建JobDetail
		JobDetail jobDetail = JobBuilder.newJob(JobBean.class).withIdentity(jobKey).build();
		// 附带参数
		JobDataMap jobDataMap = jobDetail.getJobDataMap();
		jobDataMap.put(JobConstant.HANDLER, executorHandler);
		jobDataMap.put(JobConstant.PARAMS, executorParam);
		// 加入调度
		try {
			scheduler.scheduleJob(jobDetail, trigger);
			LOG.info(JobMsg.ADD_SUCCESS, jobDetail, trigger);
		} catch (SchedulerException e) {
			LOG.error(JobMsg.ADD_FAIL, jobGroup, jobName, e);
			throw new BizRuntimeException(JobMsg.JOB_ADD_ERROR);
		}
	}

	/**
	 * 删除任务，任务不存在返回false
	 *
	 * @param jobName
	 * @param jobGroup
	 * @return
	 */
	public boolean removeJob(String jobName, String jobGroup) {
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		boolean result = false;
		if (checkExists(jobName, jobGroup)) {
			try {
				result = scheduler.unscheduleJob(triggerKey);
				LOG.info(JobMsg.REMOVE_SUCCESS, jobGroup, jobName);
			} catch (SchedulerException e) {
				LOG.error(JobMsg.REMOVE_FAIL, jobGroup, jobName, e);
				throw new BizRuntimeException(JobMsg.JOB_REMOVE_ERROR);
			}
		}
		return result;
	}

	/**
	 * 暂停任务，任务不存在返回false
	 *
	 * @param jobName
	 * @param jobGroup
	 * @return
	 */
	public boolean pauseJob(String jobName, String jobGroup) {
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		boolean result = false;
		if (checkExists(jobName, jobGroup)) {
			try {
				scheduler.pauseTrigger(triggerKey);
				result = true;
				LOG.info(JobMsg.PAUSE_SUCCESS, jobGroup, jobName);
			} catch (SchedulerException e) {
				LOG.error(JobMsg.PAUSE_FAIL, jobGroup, jobName, e);
				throw new BizRuntimeException(JobMsg.JOB_PAUSE_ERROR);
			}
		}
		return result;
	}

	/**
	 * 恢复任务，任务不存在返回false
	 *
	 * @param jobName
	 * @param jobGroup
	 * @return
	 */
	public boolean resumeJob(String jobName, String jobGroup) {
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		boolean result = false;
		if (checkExists(jobName, jobGroup)) {
			try {
				scheduler.resumeTrigger(triggerKey);
				result = true;
				LOG.info(JobMsg.RESUME_SUCCESS, jobGroup, jobName);
			} catch (SchedulerException e) {
				LOG.error(JobMsg.RESUME_FAIL, jobGroup, jobName, e);
				throw new BizRuntimeException(JobMsg.JOB_RESUME_ERROR);
			}
		}
		return result;
	}
 
	/**
	 * 手动触发任务执行一次，如果任务不存在将添加一次性任务
	 *
	 * @param jobName
	 * @param jobGroup
	 * @param executorHandler	任务执行器(BeanName，实现execute(param)方法)
	 * @param executorParam		任务执行参数，由Handler自行解析
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月15日
	 */
	public void triggerJob(String jobName, String jobGroup, String executorHandler, String executorParam) {
		JobKey jobKey = new JobKey(jobName, jobGroup);
		//检查任务是否存在
		if (checkExists(jobKey.getName(), jobKey.getGroup() )) {
			//手动触发
			try {
				scheduler.triggerJob(jobKey);
				LOG.info(JobMsg.TRIGGER_SUCCESS, jobKey.getGroup(), jobKey.getName());
			} catch (SchedulerException e) {
				LOG.error(JobMsg.TRIGGER_ERROR, jobKey.getGroup(), jobKey.getName(), e);
				throw new BizRuntimeException(JobMsg.JOB_RUN_ERROR);
			}
		}else{
			//添加为一次性任务
			this.addJob(jobName, jobGroup, TriggerType.NONE, null, null, null, null, null, executorHandler, executorParam);
		}
	}

	/**
	 * 检查任务是否存在
	 *
	 * @param jobName
	 * @param jobGroup
	 * @return
	 * @throws SchedulerException
	 */
	public boolean checkExists(String jobName, String jobGroup) {
		try {
			return scheduler.checkExists(TriggerKey.triggerKey(jobName, jobGroup));
		} catch (SchedulerException e) {
			LOG.error(JobMsg.CHECK_ERROR, jobGroup, jobName, e);
			throw new BizRuntimeException(JobMsg.JOB_CHECK_ERROR);
		}
	}

	/**
	 * 获取任务状态
	 *
	 * @param jobName
	 * @param jobGroup
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年7月14日
	 */
	public TriggerState getTriggerState(String jobName, String jobGroup) {
		try {
			// 获取触发状态
			return scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup));
		} catch (SchedulerException e) {
			LOG.error(JobMsg.JOB_STATUS_ERROR+e.getMessage(), e);
			return TriggerState.ERROR;
		}
	}
	
	/**
	 * 根据不同的触发模式，获取任务触发器
	 * 
	 * @param triggerKey
	 * @param triggerType		任务触发模式：组合time,timeout,cron,count,endTime,forever
	 * @param triggerTime		任务触发时机：指定时间
	 * @param triggerEndTime	任务终止时间
	 * @param triggerTimeout	任务触发时机：timeout秒后
	 * @param triggerCount	任务触发次数
	 * @param triggerCron	任务执行时机：CRON
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月14日
	 */
	public static Trigger getTrigger(TriggerKey triggerKey, String triggerType, LocalDateTime triggerTime, LocalDateTime triggerEndTime, Integer triggerTimeout, Integer triggerCount, String triggerCron){
		Trigger trigger;
		SimpleScheduleBuilder scheduleBuilder;
		CronScheduleBuilder cronScheduleBuilder;
		// 触发类型
		switch (triggerType) {
		case TriggerType.NONE:
			// 创建Trigger：立即触发一次(手动触发)
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startNow().build();
			break;
		case TriggerType.TIME_ONCE:
			// 创建Trigger：指定时间执行一次
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).build();
			break;
		case TriggerType.TIMEOUT_ONCE:
			// 调度模式：指定间隔执行一次
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).withRepeatCount(1);
			// 创建Trigger：使用调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIME_TIMEOUT_ONCE:
			// 调度模式：指定间隔执行一次
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).withRepeatCount(1);
			// 创建Trigger：指定起始时间和调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIMEOUT_FOREVER:
			// 调度模式：指定永久间隔执行
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).repeatForever();
			// 创建Trigger：指定调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIMEOUT_COUNT:
			// 调度模式：指定间隔执行count次
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).withRepeatCount(triggerCount);
			// 创建Trigger：指定调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIME_TIMEOUT_FOREVER:
			// 调度模式：指定永久间隔执行
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).repeatForever();
			// 创建Trigger：指定起始时间和调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIME_TIMEOUT_COUNT:
			// 调度模式：指定间隔执行count次
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).withRepeatCount(triggerCount);
			// 创建Trigger：指定起始时间和调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIMEOUT_ENDTIME:
			// 调度模式：指定永久间隔执行
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).repeatForever();
			// 创建Trigger：指定调度模式和结束时间
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).endAt(LocalDateUtil.toDate(triggerEndTime)).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.TIME_TIMEOUT_ENDTIME:
			// 调度模式：指定永久间隔执行
			scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
			scheduleBuilder.withIntervalInSeconds(triggerTimeout).repeatForever();
			// 创建Trigger：指定起始时间和调度模式和结束时间
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).endAt(LocalDateUtil.toDate(triggerEndTime)).withSchedule(scheduleBuilder).build();
			break;
		case TriggerType.CRON:
			// 创建CRON调度器，忽略调度终止过程中忽略的调度
			cronScheduleBuilder = CronScheduleBuilder.cronSchedule(triggerCron).withMisfireHandlingInstructionDoNothing();
			// 创建Trigger：指定调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
			break;
		case TriggerType.TIME_CRON:
			// 创建CRON调度器，忽略调度终止过程中忽略的调度
			cronScheduleBuilder = CronScheduleBuilder.cronSchedule(triggerCron).withMisfireHandlingInstructionDoNothing();
			// 创建Trigger：指定起始时间和调度模式
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).withSchedule(cronScheduleBuilder).build();
			break;
		case TriggerType.CRON_ENDTIME:
			// 创建CRON调度器，忽略调度终止过程中忽略的调度
			cronScheduleBuilder = CronScheduleBuilder.cronSchedule(triggerCron).withMisfireHandlingInstructionDoNothing();
			// 创建Trigger：指定调度模式和结束时间
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).endAt(LocalDateUtil.toDate(triggerEndTime)).withSchedule(cronScheduleBuilder).build();
			break;
		case TriggerType.TIME_CRON_ENDTIME:
			// 创建CRON调度器，忽略调度终止过程中忽略的调度
			cronScheduleBuilder = CronScheduleBuilder.cronSchedule(triggerCron).withMisfireHandlingInstructionDoNothing();
			// 创建Trigger：指定起始时间和调度模式和结束时间
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(LocalDateUtil.toDate(triggerTime)).endAt(LocalDateUtil.toDate(triggerEndTime)).withSchedule(cronScheduleBuilder).build();
			break;
		default:
			throw new BizRuntimeException(JobMsg.TRIGGER_TYPE_ERROR);
		}
		return trigger;
	}

}
